#pragma once

#ifndef __THREAD_H__
#define __THREAD_H__

#include <pthread.h>
#include <string>
#include <cstring>
#include <functional>
#include <cstdio>
#include <cassert>

class Thread; // 进行声明，因为C++找内容只会向上查找而不是全文查找

class Thread {
public:
    using func_t = std::function<void* (void*)>;

private:
    pthread_t _tid; // 线程的编号，给OS使用的
    // std::string _name; // 线程的标识，给用户看的。 类似于inode和fd，key和shmid

    func_t _func; // 函数指针，或仿函数对象或lambda表达式
    void* _args; // 函数的参数

public:
    Thread() = default;

    /**
     * @param func 线程的执行函数
     * @param args 执行函数的参数
     * @param num 线程的编号
    */
    Thread(func_t func, void* args/*, int num*/)
        : _func(func)
        , _args(args)
    {}

    Thread(const Thread& thread) = delete;
    Thread& operator=(const Thread& thread) = delete;
    Thread(Thread&& thread) = default;
    Thread& operator=(Thread&& thread) = default;

    ~Thread() {
        join();
    }

public:
    void join() {
        int n = pthread_join(_tid, nullptr);
        assert(n == 0);
        (void)n;
    }

    // 在非static函数内部将this指针作为上下文传递给start_routine
    // 这样start_routine尽管是static函数，但仍能调用非static函数或成员变量
    //* 这是一种有效解决static函数不能调用非static成员的解决方法
    //* 当然构建上下文是为了传递更多的信息，如果只是想实现非static调用的话，直接将this指针作为参数传入即可
    void* start(func_t func, void* args = nullptr) {

        _func = func;
        _args = args;
        int n = pthread_create(&_tid, nullptr, start_routine, (void*)this);
        assert(n == 0);
        (void)n;
    }

private: 
    // 因为_func被包装器修饰，拥有C++的特性，所以不能像函数指针一样直接被C语言类型的系统接口所兼容
    // 因此封装了一层来执行函数
    void* run(/*void* args*/) {
        return _func(_args);
    }

    // pthread_create中执行的start_routine必须由static修饰
    // 因为pthread_create要求start_routine中的参数有且只有一个，且必须是void*类型
    // 可是start_routine是成员函数，所以有一个隐藏的this指针
    // 为了消除这个this指针，所以必须用static修饰该函数
    // 因为静态函数是没有this指针的
    static void* start_routine(void* args) {

        Thread* _this = static_cast<Thread*>(args);
        return _this->run();
    }
};

#endif